/**
 * WTF Engine
 *
 * License... etc.
 **
 * Common Network Base Classes
 **
 * Author: Sean Micklethwaite
 **/

#ifndef __net_socketH__
#define __net_socketH__

#include "kernel.h"
#include "buf.h"

namespace WtfEngine {
	/**
	 * Generic packet interface.
	 * 
	 * Defines a blob of data to be sent to any number of clients, without
	 * TCP/IP header information.
	 **/
	class IPacket : public virtual IGcObject {
		public:
			virtual unsigned getSize() const = 0;
			virtual const unsigned char * getData() const = 0;
			
			GC_INTERFACE(IPacket);
	};
	

	typedef struct IpAddress{
		unsigned int	Value;
		
		IpAddress(): Value(0) {};
		IpAddress(unsigned int v) {
			Value = v;
		};
		IpAddress(unsigned char a, unsigned char b, unsigned char c, unsigned char d) {
			Value = (a << 24) + (b << 16) + (c << 8) + d;
		};
		
		operator unsigned int () {
			return Value;
		};
	} tIpAddress;

	typedef unsigned short				tPort;

	typedef std::pair<tIpAddress, tPort> tSocketId;


	/**
	 * Generic socket interface, representing a connection to a
	 * remote machine.
	 **/
	class ISocket : public virtual IGcObject {
		public:
			typedef enum {
				UNINITIALIZED = 0,
				LISTENING,
				CONNECTING,
				CONNECTED,
				SENDING,
				RECEIVING,
				SEND_AND_RECV,
				CLOSING,
				CLOSED,
				LISTEN_ERROR,
				CONNECT_ERROR,
				SEND_ERROR,
				RECV_ERROR
			} tStatus;
			
			virtual void Listen() = 0;
			virtual void Connect(tIpAddress ip) = 0;
			virtual void Disconnect() = 0;
			
			virtual void			SendPacket(const IPacket::tRef& rPacket) = 0;
			virtual IPacket::tRef	RecvPacket() = 0;
			
			virtual tStatus		getStatus() = 0;
			virtual tIpAddress	getIpAddress() = 0;
			virtual tPort		getPort() = 0;
			
			virtual void		Destroy() = 0;

			GC_INTERFACE(ISocket);
	};
	
	template <unsigned Size>
	class RawPacket : public virtual IPacket, public StaticBuffer<unsigned char, Size> {
		public:
			RawPacket(): StaticBuffer<unsigned char, Size>() {
			};

			const unsigned char * getData() const {
				return StaticBuffer<unsigned char, Size>::getData();
			};

			unsigned getSize() const {
				return Size;
			};

			GC_AUTOSIZE(RawPacket);
	};

	class NonCopyPacket : public virtual IPacket, public NonCopyBuffer<unsigned char> {
		public:
			/// Construct packet by passing a block of data. The packet takes ownership of the data.
			NonCopyPacket(unsigned char * pData, unsigned count): NonCopyBuffer<unsigned char>(pData, count) {
			};

			const unsigned char * getData() const {
				return NonCopyBuffer<unsigned char>::getData();
			};

			unsigned getSize() const {
				return getCount();
			};

			GC_AUTOSIZE(NonCopyPacket);
	};

	
	/**** Events ****/

	class SocketEvent : public KeyedEvent<SocketEvent>
	{
		ISocket::tRef	mrSocket;
		
		public:
			explicit SocketEvent(const ISocket::tRef& rSocket):
				mrSocket(rSocket) {
			};
			virtual ~SocketEvent() {
			};
			
			/// Returns the socket relating to the event
			ISocket::tRef getSocket() const {
				return mrSocket;
			};
			
			// The plumbing
			EVENT(SocketEvent, KeyedEvent<SocketEvent>, ((int)mrSocket.getRef() - (int)pEv->mrSocket.getRef()));
			GC_AUTOSIZE(SocketEvent);
	};

	EVENT_TYPE_FORK(KeyedSocketEvent, SocketEvent, const ISocket::tRef& rSocket, rSocket);

	
	/**
	 * Socket data event - fired when a connected socket recieves data
	 **/
	class SocketDataEvent : public KeyedSocketEvent<SocketDataEvent> {
		tSocketId mSource;

		public:
			SocketDataEvent(const ISocket::tRef& rSocket):
				KeyedSocketEvent<SocketDataEvent>(rSocket)
			{
			};
			SocketDataEvent(const ISocket::tRef& rSocket, tSocketId id):
				KeyedSocketEvent<SocketDataEvent>(rSocket), mSource(id)
			{
			};
			virtual ~SocketDataEvent()
			{
			};
		
			inline tSocketId getSource() const {
				return mSource;
			};

			/// This is simply a type check (after a call to the parent)
			virtual bool Accepts (const Event& ev){
				return (SocketEvent::Accepts(ev) && (dynamic_cast<const SocketDataEvent*>(&ev) != NULL));
			};

			GC_AUTOSIZE(SocketDataEvent);
	};
	
	class SocketStatusChangeEvent : public KeyedSocketEvent<SocketStatusChangeEvent> {
		public:
			SocketStatusChangeEvent(const ISocket::tRef& rSocket):
				KeyedSocketEvent<SocketStatusChangeEvent>(rSocket) {
			};
			virtual ~SocketStatusChangeEvent() {
			};
			
			/// This is simply a type check (after a call to the parent)
			virtual bool Accepts (const Event& ev){
				return (SocketEvent::Accepts(ev) && (dynamic_cast<const SocketStatusChangeEvent*>(&ev) != NULL));
			};

			GC_AUTOSIZE(SocketStatusChangeEvent);
	};
	
	/**
	 * This event class exists to allow binding to specific socket status changes.
	 **/
	class SocketStatusChangeTo : public SocketStatusChangeEvent {
		ISocket::tStatus	mStatus;

		public:
			inline SocketStatusChangeTo(const ISocket::tRef& rSocket, ISocket::tStatus st)
				: SocketStatusChangeEvent(rSocket), mStatus(st) {
			};

			ISocket::tStatus getStatus() const {
				return mStatus;
			};

			/// Order on the socket status
			EVENT(SocketStatusChangeTo, SocketStatusChangeEvent, (mStatus - pEv->mStatus));
			GC_AUTOSIZE(SocketStatusChangeTo);
	};

	// Convenience classes
	class SocketConnectEvent : public SocketStatusChangeTo {
		public:
			SocketConnectEvent(const ISocket::tRef& rSocket): SocketStatusChangeTo(rSocket, ISocket::CONNECTED) {
			};

			GC_AUTOSIZE(SocketConnectEvent);
	};
	class SocketCloseEvent : public SocketStatusChangeTo {
		public:
			SocketCloseEvent(const ISocket::tRef& rSocket): SocketStatusChangeTo(rSocket, ISocket::CLOSED) {
			};

			GC_AUTOSIZE(SocketCloseEvent);
	};


	/**
	 * Fired when a client connects to a server socket. A new socket
	 * is created representing the client's connection.
	 **/
	class SocketClientConnectEvent: public KeyedSocketEvent<SocketClientConnectEvent> {
		ISocket::tRef	mrNewSocket;
		
		public:
			SocketClientConnectEvent(const ISocket::tRef& rSocket):
				KeyedSocketEvent<SocketClientConnectEvent>(rSocket) {
			};
			SocketClientConnectEvent(const ISocket::tRef& rSocket, const ISocket::tRef& rNewSocket):
				KeyedSocketEvent<SocketClientConnectEvent>(rSocket), mrNewSocket(rNewSocket) {
			};
			~SocketClientConnectEvent() {
			};
			
			/// Returns the new socket representing the client's connection.
			ISocket::tRef getNewSocket() const {
				return mrNewSocket;
			};
			
			GC_AUTOSIZE(SocketClientConnectEvent);
	};
};


#endif
